/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_PARTICLESYSTEM_H
#define M_PARTICLESYSTEM_H

#include "movableobject.h"
#include "plane.h"
#include "aabb.h"
#include "synchronize.h"
#include "mutex.h"

#include <list>
#include <string>

namespace Engine
{
	/**
	*   Particle system.
	*/
	class ParticleSystem: public MovableObject, public ObjectLevelLockable<Mutex>
	{
	public:
		friend class ProcessParticle;
		/**
		*   Collision Results.
		*/
		enum CollisionResult
		{
			CR_BOUNCE  = 0,
			CR_STICK   = 1,
			CR_RECYCLE = 2
		};

		/**
		*   Constructor.
		*   @param material material for current particle system.
		*   @param maxParticles maximum number of particles.
		*/
		ParticleSystem(const std::string& name, const std::string& material, unsigned int maxParticles);

		/**
		*   Destructor.
		*/
		~ParticleSystem();

		/**
		*   Retrieves the local axis-aligned bounding box for this object.
        */
        virtual const AABB& GetBoundingBox(void) const;

		/**
		*   Method for supporting visitor pattern (see scene manager).
		*/
		virtual void Accept(MovableObjectVisitor* visitor);

		/**
		*   Set one shoot mode on/off
		*/
		void SetOneShootMode(bool val);

		/**
		*   Return true if one shoot mode on
		*/
		bool GetOneShootMode();

		/**
		*   Get maximum number of particles.
		*/
		unsigned int GetMaxParticles();

		/**
		*   Number of particles emits by one shoot.
		*/
		void SetNumToRelease( unsigned int numToRelease );

		/**
		*   Return count of particles emits by one shoot.
		*/
		unsigned int GetNumToRelease( void );

		/**
		*    Time interval for shooting new particles.
		*/
		void SetReleaseInterval( REAL releaseInterval ) ;

		/**
		*    Get time interval for shooting new particles.
		*/
		REAL GetReleaseInterval();

		/**
		*   Set life cycle interval for particles.
		*/
		void SetLifeCycle( REAL lifeCycle );

		/**
		*   Return life cycle interval for particles.
		*/
		REAL GetLifeCycle( void );

		/**
		*   Set initial velocity for particles (after shooting).
		*/
		void SetVelocity( Vector3<REAL> velocity );

		/**
		*   Get initial velocity for particles.
		*/
		Vector3<REAL> GetVelocity();

		/**
		*   Set gravity.
		*/
		void SetGravity( Vector3<REAL> gravity );

		/**
		*   Get gravity.
		*/
		Vector3<REAL> GetGravity();

		/**
		*   Set wind.
		*/
		void SetWind( Vector3<REAL> wind );

		/**
		*   Get wind.
		*/
		Vector3<REAL> GetWind();

		/**
		*   Set air resistance.
		*/
		void SetAirResistence( bool airResistence );

		/**
		*   Get air resistance.
		*/
		bool GetAirResistence() ;

		/**
		*    If not 0 add random direction to exploding.
		*/
		void SetVelocityVar( REAL velocityVar );

		/**
		*   Get value of random direction to exploding.
		*/
		REAL GetVelocityVar();

		/**
		*   Set start point for emit.
		*/
		void SetStartPoint(const Vector3<REAL>& pos);

		/**
		*   Get start point for emit.
		*/
		const Vector3<REAL>& GetStartPoint();

		/**
		*   Add collision plane.
		*/
		void AddCollisionPlane( const Plane& plane, REAL bounceFactor = 1.0f, CollisionResult collisionResult = CR_BOUNCE );

		/**
		*    Restart system.
		*/
		void RestartParticleSystem(void);

		/**
		*   Update particles(position, velocity ...)
		*/
		void Simulate();

		/**
		*   Set emulation step, used when update.
		*/
		void SetStepSize(REAL val);

		/**
		*   Update object internal state, called form UpdateSceneGraph
		*/
		virtual void Update();
	public:

		/**
		*   Helper class, plane specific for particles.
		*/
		struct PSPlane
		{
			REAL bounceFactor;     ///< Coefficient of restitution (or how bouncy the plane is)
			CollisionResult collisionResult;  ///< What will particles do when they strike the plane
			Plane plane;
		};
		typedef std::list<PSPlane> PlaneList;

		struct Particle
		{
			Vector3<REAL> curPos;    ///< Current position of particle
			Vector3<REAL> curVel;    ///< Current velocity of particle
			REAL initTime;  ///< Time of creation of particle
			operator Vector3<float> ()const
			{
				return Vector3<float>(static_cast<float>(curPos.x),static_cast<float>(curPos.y),static_cast<float>(curPos.z));
			}
		};
		typedef std::list<Particle> ParticleList;
		typedef std::vector<Vector3<float> > ParticlePosList;

	private:

		/**
		*   Internal method by which the movable object must add Renderable subclass instances to the rendering queue.
        */
		virtual void UpdateRenderQueue(RenderQueue* queue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList);

		/**
		*   Class for rendering particles
		*/
		class ParticleRenderable: public Renderable
		{
		public:
			ParticleRenderable(MaterialPtr material, unsigned int count);
			~ParticleRenderable();
			void Update(const ParticlePosList& particles, const Matrix4<REAL>& transform);
			virtual const MaterialPtr GetMaterial(void) const {return m_material;}
			virtual RenderDataPtr GetRenderData() const {return m_renderData;}
			virtual const Matrix4<REAL>& GetWorldTransform() const {return m_transform;}
			virtual const HardWareBufferPtr GetVertexData() const {return m_vertexData;}
			virtual const HardWareBufferPtr GetIndexData() const {return m_indexData;}
			virtual const VertexDeclarationPtr GetVertexDeclaration() const {return m_vertexDeclaration;}
		private:
			Matrix4<REAL> m_transform;
			MaterialPtr m_material;
			RefPtr<RenderData, WeakPtrExtension> m_renderData;
			RefPtr<VertexDeclaration,WeakPtrExtension> m_vertexDeclaration;
			RefPtr<HardWareBuffer,WeakPtrExtension> m_vertexData;
			RefPtr<HardWareBuffer,WeakPtrExtension> m_indexData;
		};

	private:

		ParticleList    m_activeList;
		ParticlePosList m_stablePosList;
		Matrix4<REAL> m_stableTransform;
		PlaneList     m_planes;
		REAL          m_currentTime;
		REAL          m_lastUpdate;
		MaterialPtr   m_material;
		AABB          m_boundingBox;
		bool          m_oneShootMode;
		bool          m_stopEmit;

		REAL          m_step;

		// Particle Attributes
		unsigned int  m_maxParticles;
		unsigned int  m_numToRelease;
		REAL          m_releaseInterval;
		REAL          m_lifeCycle;
		Vector3<REAL> m_velocity;
		Vector3<REAL> m_gravity;
		Vector3<REAL> m_wind;
		Vector3<REAL> m_startPoint;
		bool          m_airResistence;
		REAL          m_velocityVar;

		RefPtr<ParticleRenderable,WeakPtrExtension>     m_renderable;
	};

	/******************************************************************************************************/

	/**
	*   Factory object for creating particles
	*/
	class ParticleSystemFactory : public MovableObjectFactory
	{
	public:
		ParticleSystemFactory():m_type("ParticleSystem") {}
		~ParticleSystemFactory() {}

		/**
		*   Returns the factory type.
		*/
		virtual const std::string& GetType() const { return m_type; }

		/**
		*   Creates a new object.
		*   @param name Name of the object to create
		*   @return	An object created by the factory. The type of the object depends on	the factory.
		*/
		virtual ParticleSystem* CreateInstance( const std::string& name );
	protected:
		std::string m_type;
	};
}

#endif
